home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Utilities / Winter Shell 1.0d2 / Source / Libraries / WindowLib / WindowPositionLib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-14  |  13.4 KB  |  396 lines  |  [TEXT/KAHL]

  1. /*    functions for positioning and sizing windows
  2.     94/01/08 aih - added function to determine if a window can be dragged
  3.                      - moved WinCouldDrag from WindowZoomLib.c
  4.                      - moved several window positioning and sizing related
  5.                      functions from WindowLib.c
  6.     93/11/12 aih - split from WindowLib.c */
  7.  
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include "DrawLib.h"
  11. #include "MathLib.h"
  12. #include "MemoryLib.h"
  13. #include "RectangleLib.h"
  14. #include "ScreenLib.h"
  15. #include "WindowLib.h"
  16. #include "WindoidWDEF.h"
  17.  
  18. /* Stagger the window within the rectangle 'staggerRect'. The window's new
  19.     rectangle is placed in the 'newRect' parameter. The window's minimum
  20.     and maximum sizes are determined by 'sizeRect'. If all the slots in the
  21.     stagger rectangle are full then 'staggerRect' is expanded to the default
  22.     new rectangle for the window (ie, WinNewRect() is called). If the
  23.     'constantSize' parameter is true then the window's size is preserved
  24.     while it is staggered, so that only its position changes; otherwise,
  25.     the top left of the window is moved while its bottom right remains fixed.
  26.     The 'newRect' parameter should initially contain the window's port
  27.     rectangle (the rectangle specifies the width and height of the window
  28.     when 'constantSize' is true).
  29.     
  30.     The running time of the algorithm is around O(Nslots * Nwindows^2),
  31.     but it could be made O(n + n log n) if the windows were scanned
  32.     and then sorted (n is number of windows), but it would take space
  33.     proportional to n.
  34.     
  35.     Based on the Human Interface Notes, with ideas from code for
  36.     Disinfectant v2.4 by John Norstad and from C/Shell by
  37.     MacDTS distributed with the 7.0b4 Beta CD (February 1991). */
  38. static void WinStaggerInRect(WindowPtr window, Boolean constantSize,
  39.     const Rect *sizeRect, const Rect *pstaggerRect, Rect *newRect)
  40. {
  41.     WindowPtr check;        /* window being checked */
  42.     Rect        staggerRect;/* rectangle to stagger in */
  43.     Rect        checkRect;    /* content rectangle of window being checked */
  44.     Point        delta;        /* distance between window being checked and slot */
  45.     short        occupied;    /* number of windows occupying the slot being checked */
  46.     short        layer;        /* number of windows which can be contained in a slot */
  47.     Point        margin;        /* margin between each slot */
  48.  
  49.     require(WinValid(window));
  50.     require(RectValid(pstaggerRect));
  51.     require(RectValid(newRect));
  52.         
  53.     /* initialize margin */
  54.     margin.h = 12;
  55.     margin.v = 16;
  56.  
  57.     /* Look for layer with smallest number of windows in one of its slots.
  58.         This loop must be executed at least once. The proof that this
  59.         loop will terminate is that there are a finite number of
  60.         visible windows (less than SHRT_MAX), and 'occupied' is bounded
  61.         between 0 and the number of windows, whereas layer is bounded
  62.         between 1 and SHRT_MAX. */
  63.     staggerRect = *pstaggerRect;
  64.     layer = 1;
  65.     do {
  66.     
  67.         /* the stagger rectangle must be at least the minimum size */
  68.         check(RectWidth(&staggerRect) >= sizeRect->left);
  69.         check(RectHeight(&staggerRect) >= sizeRect->top);
  70.         
  71.         /* If maintaining a constant size for the window then start from the
  72.             top left of the stagger rectangle. Otherwise, start with the largest
  73.             possible rectangle, which is simply the entire stagger rectangle
  74.             clipped to the size rectangle. */
  75.         if (constantSize) {
  76.             newRect->bottom = staggerRect.top + RectHeight(newRect);
  77.             newRect->right = staggerRect.left + RectWidth(newRect);
  78.         }
  79.         else {
  80.             newRect->bottom = staggerRect.top + min(sizeRect->bottom, RectHeight(newRect));
  81.             newRect->right = staggerRect.left + min(sizeRect->right, RectWidth(newRect));
  82.         }
  83.         newRect->top = staggerRect.top;
  84.         newRect->left = staggerRect.left;
  85.         
  86.         do {
  87.             
  88.             /* check all visible windows to see if they fall within the current
  89.                 slot */
  90.             occupied = 0;
  91.             check = WinFirstVisible(WinLayer(window));
  92.             while (occupied < layer && check) {
  93.                 
  94.                 /* check if this slot is occupied */
  95.                 WinContentRect(check, &checkRect);
  96.                 delta.h = checkRect.left - newRect->left;
  97.                 delta.v = checkRect.top - newRect->top;
  98.                 if (abs(delta.h) < margin.h && abs(delta.v) < margin.v) {
  99.                 
  100.                     /* If the window that took our slot is closer to the lower-right
  101.                         corner than we are, then use this window's location as the
  102.                         basis for the slots from now on, provided that the new
  103.                         window's rectangle would not become too small. This will
  104.                         align new windows with previous windows that are not gridded
  105.                         to the default slot positions. The check for > 0 is necessary
  106.                         to prevent bouncing between two existing windows. This check
  107.                         guarantees that we are progressing with the evaluation. */
  108.                     occupied++;
  109.                     if (delta.h > 0 && delta.v > 0 &&
  110.                           (RectWidth(newRect) - delta.h >= sizeRect->left ||
  111.                             RectHeight(newRect) - delta.v >= sizeRect->top))
  112.                     {
  113.                         if (constantSize)
  114.                             OffsetRect(newRect, delta.h, delta.v);
  115.                         else {
  116.                             newRect->left += delta.h;
  117.                             newRect->top += delta.v;
  118.                         }
  119.                     }
  120.                 }
  121.                 
  122.                 /* check next visible window */
  123.                 check = WinNextVisible(check);
  124.             }
  125.             
  126.             /* advance to next slot if current slot is occupied by too many
  127.                 windows */
  128.             if (occupied >= layer) {
  129.                 if (constantSize)
  130.                     OffsetRect(newRect, margin.h, margin.v);
  131.                 else {
  132.                     newRect->left += margin.h;
  133.                     newRect->top += margin.v;
  134.                 }
  135.             }
  136.  
  137.             /* repeat while the current slot is occupied and the new rectangle
  138.                 isn't too small and the new rectangle is contained within the
  139.                 stagger rectangle */
  140.         } while (occupied >= layer &&
  141.                     RectWidth(newRect) > sizeRect->left &&
  142.                     RectHeight(newRect) > sizeRect->top &&
  143.                     RectWithin(newRect, &staggerRect));
  144.         
  145.         /* if all slots are occupied, and this is the first layer, then
  146.             expand the stagger rectangle to the entire screen and try again
  147.             (redoing the first layer) */
  148.         if (occupied >= layer && layer == 1) {
  149.             /* make sure the stagger rectangle isn't already the new window
  150.                 rectangle */
  151.             WinNewRect(window, newRect);
  152.             if (! EqualRect(&staggerRect, newRect)) {
  153.                 staggerRect = *newRect;
  154.                 layer = 0;
  155.             }
  156.         }
  157.         
  158.     } while (occupied >= layer++);
  159.  
  160.     /* adjust bottom left so rectangle doesn't exceed the maximum size */
  161.     newRect->bottom = newRect->top + min(RectHeight(newRect), sizeRect->bottom);
  162.     newRect->right = newRect->left + min(RectWidth(newRect), sizeRect->right);
  163.     
  164.     /* lots of assertions to ensure that this function does what it promises */
  165.     
  166.     /* ensure that if the window's size wasn't supposed to change that it
  167.         really didn't */
  168.     if (constantSize) {
  169.         Rect portRect;
  170.         WinPortRect(window, &portRect);
  171.         ensure(RectWidth(&portRect) == RectWidth(newRect));
  172.         ensure(RectHeight(&portRect) == RectHeight(newRect));
  173.     }
  174.     
  175.     /* the new rectangle must be at least as large as the minimum size and
  176.         no larger than the maximum size */
  177.     ensure(sizeRect->left <= RectWidth(newRect) && RectWidth(newRect) <= sizeRect->right);
  178.     ensure(sizeRect->top <= RectHeight(newRect) && RectHeight(newRect) <= sizeRect->bottom);
  179.  
  180.     /* ensure that the new rectangle is entirely contained within the stagger
  181.         rectangle */
  182.     ensure(RectWithin(&staggerRect, pstaggerRect));
  183.     ensure(RectWithin(newRect, &staggerRect));
  184. }
  185.  
  186. /* Stagger the window in the current screen, using width and height as the
  187.     initial size for the window. See WinStaggerInRect for more details. */
  188. void WinStagger(WindowPtr window, Boolean constantSize,
  189.     short width, short height)
  190. {
  191.     Rect newRect;
  192.     Rect sizeRect;
  193.     Rect staggerRect;
  194.     Rect oldSize, newSize;
  195.     
  196.     WinPortRect(window, &oldSize);
  197.     WinSizeRect(window, &sizeRect);
  198.     WinNewRect(window, &staggerRect);
  199.     width = min(min(max(width, sizeRect.left), sizeRect.right), RectWidth(&staggerRect));
  200.     height = min(min(max(height, sizeRect.top), sizeRect.bottom), RectHeight(&staggerRect));
  201.     SetRect(&newRect, 0, 0, width, height);
  202.     WinStaggerInRect(window, constantSize, &sizeRect, &staggerRect, &newRect);
  203.     WinMove(window, newRect.left, newRect.top);
  204.     WinSize(window, RectWidth(&newRect), RectHeight(&newRect));
  205.     WinPortRect(window, &newSize);
  206.     WinResize(window, RectWidth(&newSize) - RectWidth(&oldSize),
  207.                             RectHeight(&newSize) - RectHeight(&oldSize));
  208.     WinZoomReset(window); 
  209.     WinZoomRemember(window);
  210. }
  211.  
  212. /* position window in current screen */
  213. void WinPosition(WindowPtr window, short h, short v)
  214. {
  215.     Rect structure, content, drag, new;
  216.     Point pt;
  217.     
  218.     require(WinValid(window));
  219.     WinRectangles(window, &structure, &content, &drag, &new);
  220.     RectPositionInScreen(&structure, 2, 3, &pt);
  221.     pt.h += content.left - structure.left;
  222.     pt.v += content.top - structure.top;
  223.     WinMove(window, pt.h, pt.v);
  224.     WinZoomReset(window); 
  225.     WinZoomRemember(window);
  226. }
  227.  
  228. /* True if the window could be dragged were its size and position to be set
  229.     to the 'bounds' rectangle (given in global coordinates). */
  230. Boolean WinCouldDrag(WindowPtr window, const Rect *bounds)
  231. {
  232.     RgnHandle dragRgn = NULL;        /* window's drag region */
  233.     RgnHandle grayRgn = NULL;        /* copy of gray region */
  234.     Rect            strucRect;    /* bounding box of window's structure region */
  235.     Rect            contRect;    /* bounding box of window's content region */
  236.     Rect            dragRect;    /* bounding box of window's drag region */
  237.     Boolean        result = false;
  238.     
  239.     require(WinValid(window));
  240.  
  241.     /* window must have a drag region */
  242.     if (WinHasDrag(window)) {
  243.  
  244.         /* calculate drag region */
  245.         /* program_note: this is a little messy and not the best place
  246.                               to do the calculation, but we can't just call
  247.                               WinDragRect since the window doesn't exist yet */
  248.         dragRgn = BeginRgn();
  249.         grayRgn = BeginRgn();
  250.         dragRect = *bounds;
  251.         if (WinIsFloat(window)) {
  252.             if (WindoidDragTop(GetWVariant(window))) {
  253.                 dragRect.top -= kWindoidDragSize - 1;
  254.                 dragRect.bottom = bounds->top - 1;
  255.             }
  256.             else if (WindoidDragLeft(GetWVariant(window))) {
  257.                 dragRect.left -= kWindoidDragSize - 1;
  258.                 dragRect.right = bounds->left - 1;
  259.             }
  260.             else
  261.                 check(false);
  262.             InsetRect(&dragRect, kWindoidBorderSize, kWindoidBorderSize);
  263.         }
  264.         else {
  265.             dragRect.top -= 20;
  266.             dragRect.bottom = bounds->top - 1;
  267.         }
  268.         check(RectValid(&dragRect));
  269.         RectRgn(dragRgn, &dragRect);
  270.         
  271.         /* copy the gray region and inset the copy by SCREEN_MARGIN pixels to
  272.             get the region within which the user can drag windows */
  273.         CopyRgn(GetGrayRgn(), grayRgn);
  274.         InsetRgn(grayRgn, SCREEN_MARGIN, SCREEN_MARGIN);
  275.         
  276.         /* the gray and drag regions need to intersect */
  277.         SectRgn(grayRgn, dragRgn, dragRgn);
  278.         result = ! EmptyRgn(dragRgn);
  279.         
  280.         EndRgn(dragRgn);
  281.         EndRgn(grayRgn);
  282.     }
  283.     return(result);
  284. }
  285.  
  286. /* true if the window can be dragged from its current position, which requires
  287.     that the window is visible and that its drag region intersect the area within
  288.     which the window could be dragged */
  289. Boolean WinCanDrag(WindowPtr window)
  290. {
  291.     Rect bounds;
  292.     
  293.     WinContentRect(window, &bounds);
  294.     return(WinVisible(window) && WinCouldDrag(window, &bounds));
  295. }
  296.  
  297.  
  298. /* This is the same as MoveWindow except it always passes false as the last
  299.     parameter to MoveWindow so as not to disrupt window layers. */
  300. void WinMove(WindowPtr window, short left, short top)
  301. {
  302.     require(WinValid(window));
  303.     MoveWindow(window, left, top, false);
  304.     ensure(WinValid(window));
  305. }
  306.  
  307. /* Set the size of the window. The objects in the window are responsible
  308.     for invalidating the areas they formerly occupied and the new areas
  309.     they occupy. This function only invalidates the grow region, in
  310.     addition to any new content region. */
  311. void WinSize(WindowPtr window, short width, short height)
  312. {
  313.     GrafPtr port;
  314.     Rect oldGrowRect;
  315.     Rect newGrowRect;
  316.  
  317.     require(WinValid(window));
  318.     GetPort(&port);
  319.     SetPort(window);
  320.     WinPortRect(window, &oldGrowRect);
  321.     SizeWindow(window, width, height, true);
  322.     WinPortRect(window, &newGrowRect);
  323.     if (WinHasGrow(window)) {
  324.         oldGrowRect.top = oldGrowRect.bottom - 15;
  325.         oldGrowRect.left = oldGrowRect.right - 15;
  326.         newGrowRect.top = newGrowRect.bottom - 15;
  327.         newGrowRect.left = newGrowRect.left - 15;
  328.         EraseRect(&oldGrowRect);
  329.         InvalRect(&oldGrowRect);
  330.         EraseRect(&newGrowRect);
  331.         InvalRect(&newGrowRect);
  332.     }
  333.     SetPort(port);
  334.     ensure(WinValid(window));
  335. }
  336.  
  337. /* Drag the window. This replaces DragWindow since DragWindow selects the
  338.     window, and selecting the window would disrupt window layers. */
  339. void WinDrag(WindowPtr window, Point where)
  340. {
  341.     Point            offset;            /* amount to offset window */
  342.     Rect            stopRect;        /* empty stop rectangle */
  343.     Rect            dragRect;        /* rectangle enclosing window's drag region */
  344.     Rect             contentRect;    /* window's content rectangle */
  345.     Rect            structureRect;    /* window's structure rectangle */
  346.     WindowPtr    above = NULL;    /* for calculating clipRgn */
  347.     RgnHandle    clipRgn = NULL;/* region to clip to */
  348.     
  349.     require(WinValid(window));
  350.     require(WinVisible(window));
  351.  
  352.     /* calculate rectangles */
  353.     SetRect(&stopRect, 0, 0, 0, 0);
  354.     WinDragRect(window, &dragRect);
  355.     WinContentRect(window, &contentRect);
  356.     WinStructureRect(window, &structureRect);
  357.     
  358.     /* calculate clip region by removing the structure regions of
  359.         all of the windows above this window */
  360.     clipRgn = BeginRgn();
  361.     CopyRgn(GetGrayRgn(), clipRgn);
  362.     above = FrontWindow();
  363.     while (above != window) {
  364.         WindowPeek win = (WindowPeek) above;
  365.         check(win != NULL);
  366.         if (win->visible)
  367.             DiffRgn(clipRgn, win->strucRgn, clipRgn);
  368.         above = (WindowPtr) win->nextWindow;
  369.     }
  370.     
  371.     /* drag outline */
  372.     offset = DragRect(&structureRect, &dragRect, GetGrayRgn(), clipRgn, where);
  373.     
  374.     /* move window */
  375.     if (offset.h || offset.v) {
  376.         WinMove(window, contentRect.left + offset.h,
  377.                              contentRect.top + offset.v);
  378.     }
  379.     EndRgn(clipRgn);
  380.     ensure(WinValid(window));
  381. }
  382.  
  383. /* grow the window */
  384. void WinGrow(WindowPtr window, Point where)
  385. {
  386.     long grow;
  387.     Rect sizeRect;
  388.     
  389.     require(WinValid(window));
  390.     WinSizeRect(window, &sizeRect);
  391.     grow = GrowWindow(window, where, &sizeRect);
  392.     if (grow)
  393.         WinSize(window, (*(Point *) &grow).h, (*(Point *) &grow).v);
  394.     ensure(WinValid(window));
  395. }
  396.